package state;

import java.util.Vector;

import state.component.BottomBar;
import state.component.Component;
import state.component.Menu;
import state.component.ScrollBar;
import state.component.TouchPad;
import state.util.Color;

public class Manager {
	private Sprite baseSprite;
	private Sprite root;
	private Sprite last;
	private int backGroundColor = Color.WHITE_CODE;
	private GameForm parent;
	private Pointer pointer;
	private boolean useMouseMoveEvent = false;
	private Image backgroundImage;
	private Vector scrolls;
	
	private BottomBar bottomBar;
	private TouchPad touchPad;
	
	private boolean isContinueEvent;
	
	public Manager(GameForm parent) {
		this.parent = parent;
		baseSprite = new Sprite(this, 0, 0, GameConstants.SCREEN_WIDTH, GameConstants.SCREEN_HEIGHT);
		baseSprite.setDrawListener(new DrawListener() {
			public void paint(Sprite source, Graphics g) {
				Manager.this.parent.draw(g);
			}
		});
		root = baseSprite;
		last = root;
	}
	
	public void enableTouchPad() {
	  if (touchPad == null && GameGlobal.systemCanvas.hasPointerEvents() && GameGlobal.systemCanvas.hasPointerMotionEvents()) {
	    touchPad = new TouchPad(this);
	  }
	}
	
	public void disableTouchPad() {
	  if (touchPad != null) {
	    touchPad.detroy();
	    touchPad = null;
	  }
	}
	
	public void stopEvent() {
	  isContinueEvent = false;
	}
	
	/**
	 * Sử dụng pointer trong State hiện tại
	 */
	public void enablePointer() {
		if (pointer == null) {
			pointer = new Pointer(this);
			if (useMouseMoveEvent) {
				pointer.useMouseMoveEvent();
			}
		}
	}
	
	/**
	 * Hủy sử dụng pointer
	 */
	public void disablePointer() {
		if (pointer != null) {
			remove(pointer);
			pointer.detroy();
			pointer = null;
		}
	}
	
	public Sprite getBaseSprite() {
		return baseSprite;
	}
	
	/**
	 * Lấy pointer đang xử dụng của manager
	 * 
	 * @return Pointer đang xử dụng của manager, trả về null nếu không có pointer nào được xử dụng
	 */
	public Pointer getPointer() {
		return pointer;
	}
	
	private Clip reuseClip = new Clip(0, 0, 0, 0);

	/**
	 * Tự động cập nhật tất cả các thay đổi của các Sprite trong State lên Graphic.
	 * Phương thức này sẽ tự động cập nhật Graphic nên màn hình nếu tham số tự động cập nhật màn hình isAutoFlushGraphic được set là true
	 */
	public void updateFullScreen() {
		if (!parent.isRunning) {
			return;
		}
		
		Graphics g = getGraphics();
		if (g == null) {
			return;
		}
		
		Clip.getClip(g, reuseClip);
		reuseClip.clipRect(g, parent.realX, parent.realY, parent.width, parent.height);
		g.setColor(backGroundColor);
		g.fillRect(parent.realX, parent.realY, parent.width, parent.height);
		
		if (backgroundImage != null) {
			g.drawImage(backgroundImage, parent.realX, parent.realY, GameConstants.TOP_LEFT_ANCHOR);
		}
		
		
		if (root != null) {
			Sprite current = root;
			while (current != null) {
			  g.clipRect(current.getRealX(), current.getRealY(), current.getWidth(), current.getHeight());
				current.paint(g);
				current = current.next;
				reuseClip.applyClip(g);
			}
		}
		reuseClip.applyClip(g);
		
		if (scrolls != null) {
		  for (int i = 0; i < scrolls.size(); i++) {
        ScrollBar scrollBar = (ScrollBar) scrolls.elementAt(i);
        scrollBar.reset();
      }
		}

		if (bottomBar != null) {
		  bottomBar.draw(g);
		}
		
		if (touchPad != null) {
		  touchPad.paint(g);
		}
	}
	
	public void addScrollBar(ScrollBar scrollBar) {
	  if (scrolls == null) {
	    scrolls = new Vector();
	  }
	  scrolls.addElement(scrollBar);
	}
	
	public BottomBar getBottomBar() {
    return bottomBar;
  }

  public void setBottomBar(BottomBar bottomBar) {
    this.bottomBar = bottomBar;
  }

  public void setBackgroundImage(Image backgroundImage) {
		this.backgroundImage = backgroundImage;
	}

	/**
	 * Lấy Graphic đang xử dụng hiện tại, có thể là Graphic màn hình hoặc Screen ảo đã được thiết lập
	 * 
	 * @return Graphic đang xử dụng
	 */
	public Graphics getGraphics() {
		Graphics g = null;
		if (parent.getScreen() != null) {
			g = parent.getScreen().getGraphics();
		} else {
			g = GameGlobal.systemCanvas.getGraphic();
		}
		return g;
	}
	
	/**
	 * Thiết lập mầu nền cho State
	 * 
	 * @param color - Mầu nền
	 */
	public void setBackGroundColor(int color) {
		backGroundColor = color;
	}
	
	/**
	 * Lấy State hiện tại của manager này
	 * 
	 * @return State hiện tại của manager
	 */
	public GameForm getParent() {
		return parent;
	}
	
	public final void setFocus(Component component) {
    if (root != null) {
      Sprite current = root;
      while (current != null) {
        if (current instanceof Component) {
          ((Component)current).setFocused(false);
        }
        current = current.next;
      }
    }
    component.setFocused(true);
  }

	public final void doKeyPressed(int keyCode) {
	  isContinueEvent = true;
	  
	  if (isContinueEvent && bottomBar != null) {
      bottomBar.keyPressed(bottomBar, keyCode);
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        leftMenu.keyPressed(leftMenu, keyCode);
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        rightMenu.keyPressed(rightMenu, keyCode);
      }
    }
	  
		if (isContinueEvent && root != null) {
			Sprite current = root;
			while (isContinueEvent && current != null) {
				if (current.isVisible() && current.keyListener != null) {
					current.keyListener.keyPressed(current, keyCode);
				}
				current = current.next;
			}
		}
	}
	
	public final void doKeyReleased(int keyCode) {
	  isContinueEvent = true;
	  
	  if (isContinueEvent && bottomBar != null) {
      bottomBar.keyReleased(bottomBar, keyCode);
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        leftMenu.keyReleased(leftMenu, keyCode);
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        rightMenu.keyReleased(rightMenu, keyCode);
      }
    }
	  
		if (isContinueEvent && root != null) {
			Sprite current = root;
			while (isContinueEvent && current != null) {
				if (current.isVisible() && current.keyListener != null) {
					current.keyListener.keyReleased(current, keyCode);
				}
				current = current.next;
			}
		}
	}
	
	public final void doKeyRepeated(int keyCode) {
	  isContinueEvent = true;
	  
	  if (isContinueEvent && bottomBar != null) {
      bottomBar.keyRepeated(bottomBar, keyCode);
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        leftMenu.keyRepeated(leftMenu, keyCode);
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        rightMenu.keyRepeated(rightMenu, keyCode);
      }
    }
	  
		if (isContinueEvent && root != null) {
			Sprite current = root;
			while (isContinueEvent && current != null) {
				if (current.isVisible() && current.keyListener != null) {
					current.keyListener.keyRepeated(current, keyCode);
				}
				current = current.next;
			}
		}
	}
	
	public final void doMousePressed(int x, int y) {
	  isContinueEvent = true;
	  if (isContinueEvent && bottomBar != null) {
      if (isContinueEvent && bottomBar.mouseListener != null && bottomBar.contains(x, y)) {
        bottomBar.mouseListener.mousePressed(bottomBar, x, y);
      }
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        if (leftMenu.mouseListener != null) {
          leftMenu.mouseListener.mousePressed(leftMenu, x, y);
        }
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        if (rightMenu.mouseListener != null) {
          rightMenu.mouseListener.mousePressed(rightMenu, x, y);
        }
      }
    }
	  
		if (isContinueEvent && root != null) {
			Sprite current = last;
			while (isContinueEvent && current != null) {
				if (isContinueEvent && current.isVisible() && (current != pointer) && current.checkCollides(x, y)) {
					if (current.mouseListener != null) {
						current.mouseListener.mousePressed(current, x, y);
					}
					break;
				}
				current = current.previous;
			}
		}
	}
	
	public final void doMouseReleased(int x, int y) {
	  isContinueEvent = true;
	  
	  if (isContinueEvent && bottomBar != null) {
      if (isContinueEvent && bottomBar.mouseListener != null && bottomBar.contains(x, y)) {
        bottomBar.mouseListener.mouseReleased(bottomBar, x, y);
      }
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        if (leftMenu.mouseListener != null) {
          leftMenu.mouseListener.mouseReleased(leftMenu, x, y);
        }
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        if (rightMenu.mouseListener != null) {
          rightMenu.mouseListener.mouseReleased(rightMenu, x, y);
        }
      }
    }
	  
		if (isContinueEvent && root != null) {
			Sprite current = last;
			while (isContinueEvent && current != null) {
				if (isContinueEvent && current.isVisible() && (current != pointer) && current.checkCollides(x, y)) {
					if (current.mouseListener != null) {
						current.mouseListener.mouseReleased(current, x, y);
					}
					break;
				}
				current = current.previous;
			}
		}
	}
	
	public final void doMouseMoved(int x, int y) {
	  isContinueEvent = true;
	  
		if (isContinueEvent && root != null) {
			Sprite current = last;
			while (isContinueEvent && current != null) {
				if (isContinueEvent && current.isVisible() && (current != pointer) && current.checkCollides(x, y)) {
					if (current.mouseMoveListener != null) {
						pointer.setCurrentMoveOnItem(current);
						current.mouseMoveListener.mouseMoved(current, x, y);
					}
					break;
				}
				current = current.previous;
			}
		}
		
		if (isContinueEvent && bottomBar != null) {
      if (isContinueEvent && bottomBar.mouseMoveListener != null && bottomBar.contains(x, y)) {
        bottomBar.mouseMoveListener.mouseMoved(bottomBar, x, y);
      }
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        if (leftMenu.mouseMoveListener != null) {
          leftMenu.mouseMoveListener.mouseMoved(leftMenu, x, y);
        }
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        if (rightMenu.mouseMoveListener != null) {
          rightMenu.mouseMoveListener.mouseMoved(rightMenu, x, y);
        }
      }
    }
	}
	
	public final void doPointerDragged(int x, int y) {
	  isContinueEvent = true;
	  if (bottomBar != null) {
      if (isContinueEvent && bottomBar.touchScreenListener != null && bottomBar.contains(x, y)) {
        bottomBar.touchScreenListener.pointerDragged(bottomBar, x, y);
      }
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        if (leftMenu.touchScreenListener != null) {
          leftMenu.touchScreenListener.pointerDragged(leftMenu, x, y);
        }
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        if (rightMenu.touchScreenListener != null) {
          rightMenu.touchScreenListener.pointerDragged(rightMenu, x, y);
        }
      }
    }
	  
	  if (isContinueEvent && touchPad != null) {
	    touchPad.touchScreenListener.pointerDragged(touchPad, x, y);
	  }
	  
	  if (root != null) {
      Sprite current = root;
      while (isContinueEvent && current != null) {
        if (current.isVisible() && (current.touchScreenListener != null) && current.contains(x, y)) {
          current.touchScreenListener.pointerDragged(current, x, y);
        }
        current = current.next;
      }
    }
	}
	
  public final void doPointerPressed(int x, int y) {
    isContinueEvent = true;
    if (bottomBar != null) {
      if (isContinueEvent && bottomBar.touchScreenListener != null && bottomBar.contains(x, y)) {
        bottomBar.touchScreenListener.pointerPressed(bottomBar, x, y);
      }
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        if (leftMenu.touchScreenListener != null) {
          leftMenu.touchScreenListener.pointerPressed(leftMenu, x, y);
        }
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        if (rightMenu.touchScreenListener != null) {
          rightMenu.touchScreenListener.pointerPressed(rightMenu, x, y);
        }
      }
    }
    
    if (isContinueEvent && touchPad != null) {
      touchPad.touchScreenListener.pointerPressed(touchPad, x, y);
    }
    
    if (root != null) {
      Sprite current = root;
      while (isContinueEvent && current != null) {
        if (current.isVisible() && (current.touchScreenListener != null) && current.contains(x, y)) {
          current.touchScreenListener.pointerPressed(current, x, y);
        }
        current = current.next;
      }
    }
  }
  
  public final void doPointerReleased(int x, int y) {
    isContinueEvent = true;
    if (bottomBar != null) {
      if (isContinueEvent && bottomBar.touchScreenListener != null && bottomBar.contains(x, y)) {
        bottomBar.touchScreenListener.pointerReleased(bottomBar, x, y);
      }
      
      if (isContinueEvent && bottomBar.getLeftMenu() != null) {
        Menu leftMenu = bottomBar.getLeftMenu();
        if (leftMenu.touchScreenListener != null) {
          leftMenu.touchScreenListener.pointerReleased(leftMenu, x, y);
        }
      }
      
      if (isContinueEvent && bottomBar.getRightMenu() != null) {
        Menu rightMenu = bottomBar.getRightMenu();
        if (rightMenu.touchScreenListener != null) {
          rightMenu.touchScreenListener.pointerReleased(rightMenu, x, y);
        }
      }
    }
    
    if (isContinueEvent && touchPad != null) {
      touchPad.touchScreenListener.pointerReleased(touchPad, x, y);
    }
    
    if (root != null) {
      Sprite current = root;
      while (isContinueEvent && current != null) {
        if (current.isVisible() && (current.touchScreenListener != null) && current.contains(x, y)) {
          current.touchScreenListener.pointerReleased(current, x, y);
        }
        current = current.next;
      }
    }
  }
	
	protected final void useMouseMoveEvent() {
		useMouseMoveEvent = true;
		if (pointer != null) {
			pointer.useMouseMoveEvent();
		}
	}
	
	/**
	 * Thêm 1 LinkedSprite vào danh sách quản lý của manager.
	 * Cần lưu ý là LinkedSprite thêm vào sau sẽ được vẽ phía trên các LinkedSprite thêm vào trước đó.
	 * 
	 * @param c - LinkedSprite thêm vào danh sách quản lý
	 */
	public void append(Sprite c) {
		if (c.manager == this) {
			return;
		}
		
		if (root == null) {
			root = c;
			last = root;
		} else {
			last.next = c;
			c.previous = last;
			last = c;
		}
		c.manager = this;
		
		if (c.mouseMoveListener != null) {
			useMouseMoveEvent();
		}
	}
	
	public void insertToHead(Sprite c) {
		if (c.manager == this) {
			return;
		}
		
		if (root == null) {
			root = c;
			last = root;
		} else {
			c.next = root;
			root.previous = c;
			root = c;
		}
		c.manager = this;
		
		if (c.mouseMoveListener != null) {
			useMouseMoveEvent();
		}
	}
	
	/**
	 * Thêm 1 LinkedSprite vào danh sách quản lý của manager ở 1 vị trí sau LinkSprite source trong danh sách
	 * Cần lưu ý là các LinkedSprite được vẽ theo thứ tự index, LinkedSprite có index lớn hơn sẽ sẽ được vẽ phía trên.
	 * 
	 * @param newSprite - LinkedSprite thêm vào danh sách quản lý
	 * @param source - LinkedSprite để xác định thêm newSprite vào vị trí phía sau
	 */
	public void insertAfter(Sprite newSprite, Sprite source) {
		if (newSprite.manager == this) {
			return;
		}
		
		if (source.manager != this) {
			return;
		}
		
		newSprite.manager = this;
		if (source.next != null) {
			newSprite.next = source.next;
			newSprite.previous = source;
			source.next.previous = newSprite;
			source.next = newSprite;
		} else {
			source.next = newSprite;
			newSprite.previous = source;
		}
		
		if (newSprite.mouseMoveListener != null) {
			useMouseMoveEvent();
		}
	}
	
	/**
	 * Thêm 1 LinkedSprite vào danh sách quản lý của manager ở 1 vị trí trước LinkSprite source trong danh sách
	 * Cần lưu ý là các LinkedSprite được vẽ theo thứ tự index, LinkedSprite có index lớn hơn sẽ sẽ được vẽ phía trên.
	 * 
	 * @param newSprite - LinkedSprite thêm vào danh sách quản lý
	 * @param source - LinkedSprite để xác định thêm newSprite vào vị trí phía trước
	 */
	public void insertBefore(Sprite newSprite, Sprite source) {
		if (newSprite.manager == this) {
			return;
		}
		
		if (source.manager != this) {
			return;
		}
		
		newSprite.manager = this;
		newSprite.previous = source.previous;
		newSprite.next = source;
		source.previous.next = newSprite;
		source.previous = newSprite;
		
		if (newSprite.mouseMoveListener != null) {
			useMouseMoveEvent();
		}
	}
	
	/**
	 * Bỏ 1 LinkedSprite ra khỏi danh sách quản lý của manager.
	 * Lưu ý là LinkedSprite đã được loại bỏ sẽ không được cập nhật hiển thị nữa
	 * 
	 * @param c - LinkedSprite cần bỏ ra khỏi danh sách quản lý
	 */
	public void remove(Sprite c) {
		if ((c == null) || (c == baseSprite) || (c.manager != this)) {
			return;
		}
		
		if (root == c) {
			root = c.next;
		} else {
			c.previous.next = c.next;
		}
		
		if (last == c) {
			last = c.previous;
		} else {
			c.next.previous = c.previous;
		}
		
		c.previous = null;
		c.next = null;
		c.manager = null;
	}
	
	/**
	 * Loại bỏ tất cả các LinkedSprite đang quản lý ra khỏi danh sách
	 */
	public void removeAll() {
		if (pointer != null) {
			pointer.detroy();
			pointer = null;
		}
		useMouseMoveEvent = false;
		
		Sprite current = last;
		while (current != root) {
			current.manager = null;
			if (current.next != null) {
				current.next.previous = null;
				current.next = null;
			}
			current = current.previous;
		}
		if (root.next != null) {
			root.next.previous = null;
			root.next = null;
		}
		
		root = baseSprite;
		last = root;
	}
	
	/**
	 * Loại bỏ và detroy tất cả các LinkedSprite hiện tại trong danh sách
	 */
	public void detroyAll() {
		useMouseMoveEvent = false;
		backgroundImage = null;
		Sprite current = last;
		Sprite tmp;
		while (current != root) {
			tmp = current;
			current = current.previous;
			if (tmp.next != null) {
				tmp.next.previous = null;
				tmp.next = null;
			}
			
			if (tmp != baseSprite) {
				tmp.detroy();
			}
		}
		
		if (root != null) {
			if (root.next != null) {
				root.next.previous = null;
				root.next = null;
			}
		}
		
		root = baseSprite;
		last = root;
		pointer = null;
		scrolls = null;
	}
}
